#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <SDL_mixer.h>
#include <assert.h>
#include "scr_game.h"
#include "scr_game_state.h"
#include "scr_game_state_manager.h"
#include "scr_resource_manager.h"
#include "scr_keyboard.h"
#include "scr_mouse.h"
#include "scr_math.h"
#include "scr_game_data.h"
#include "scr_camera.h"

SCRGame* SCRGame::m_pInstance = NULL;

SCRGame::SCRGame()
	: m_pCamera(new SCRCamera())
	, m_pStateManager(NULL)
	, m_pResourceManager(NULL)
	, m_pScreen(NULL)
	, m_ShouldQuit(false)
	, m_pSharedData(NULL)
{
}

SCRGame::~SCRGame()
{
	if (m_pCamera)
	{
		delete m_pCamera;
		m_pCamera = NULL;
	}

	Terminate();
}

void SCRGame::CreateInstance()
{
	assert(NULL == m_pInstance);

	m_pInstance = new SCRGame();
}

void SCRGame::DestroyInstance()
{
	assert(m_pInstance != NULL);

	delete m_pInstance;
	m_pInstance = NULL;
}

bool SCRGame::Initialize()
{
	bool RetVal = false;

	if (SDL_Init(SDL_INIT_EVERYTHING) != -1)
	{
		m_pScreen = SDL_SetVideoMode(SCR_SCREEN_WIDTH, SCR_SCREEN_HEIGHT, SCR_SCREEN_BPP, 
			SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_ASYNCBLIT);

		if (m_pScreen)
		{
			SDL_WM_SetCaption("The Scarlet Road", NULL);
			if (!TTF_WasInit())
			{
				TTF_Init();
			}
			Mix_Init(MIX_INIT_OGG);
			Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096);
			SCRKeyboard::InitializeState();
			SCRMouse::InitializeState();
			SCRMath::Initialize();
			m_pStateManager = new SCRGameStateManager();
			m_pResourceManager = new SCRResourceManager();
			RetVal = true;
		}
	}

	return RetVal;
}

void SCRGame::Terminate()
{
	DestroySharedData();

	if (m_pStateManager)
	{
		delete m_pStateManager;
		m_pStateManager = NULL;
	}

	if (m_pResourceManager)
	{
		delete m_pResourceManager;
		m_pResourceManager = NULL;
	}

	Mix_CloseAudio();
	Mix_Quit();
	
	if (TTF_WasInit())
	{
		TTF_Quit();
	}

	if (SDL_WasInit(0))
	{
		SDL_Quit();
	}
}

void SCRGame::Run()
{
	SDL_Event Event;
	SCRGameStatePtr GameState;

	int StartTime = 0;
	int EndTime = 0;

	while (!m_ShouldQuit &&	m_pStateManager)
	{
		int Delta = EndTime - StartTime;
		if (Delta < 1)
		{
			Delta = 1;
		}
		if (Delta > 1000)
		{
			Delta = 1000;
		}

		StartTime = SDL_GetTicks();

		SCRGameStatePtr GameState = m_pStateManager->IsRunning(Delta);

		if (GameState)
		{
			if (m_pScreen)
			{
				SDL_Flip(m_pScreen);
			}

			SCRKeyboard::UpdateState();
			SCRMouse::UpdateState();

			while (SDL_PollEvent(&Event))
			{
				if (SDL_KEYDOWN == Event.type)
				{
					SCRKeyboard::mCurrentStateA[Event.key.keysym.sym] = true;
					GameState->OnKeyClick(Event.key.keysym.sym);
				}
				else if (SDL_KEYUP == Event.type)
				{
					SCRKeyboard::mCurrentStateA[Event.key.keysym.sym] = false;
					GameState->OnKeyRelease(Event.key.keysym.sym);
				}
				else if (SDL_MOUSEBUTTONDOWN == Event.type)
				{
					SCRMouse::mCurrentStateA[Event.button.button] = true;
					SCRMouse::m_X = Event.button.x;
					SCRMouse::m_Y = Event.button.y;
					GameState->OnMouseClick(SCRMouse::m_X, SCRMouse::m_Y, Event.button.button);
				}
				else if (SDL_MOUSEBUTTONUP == Event.type)
				{
					SCRMouse::mCurrentStateA[Event.button.button] = false;
					SCRMouse::m_X = Event.button.x;
					SCRMouse::m_Y = Event.button.y;
					GameState->OnMouseRelease(SCRMouse::m_X, SCRMouse::m_Y, Event.button.button);
				}
				else if (SDL_MOUSEMOTION == Event.type)
				{
					SCRMouse::m_X = Event.motion.x;
					SCRMouse::m_Y = Event.motion.y;
					GameState->OnMouseMove(SCRMouse::m_X, SCRMouse::m_Y);
				}
				else if (SDL_QUIT == Event.type)
				{
					m_ShouldQuit = true;
				}
			}
		}
		else
		{
			m_ShouldQuit = true;
		}

		m_pStateManager->ClearQueue();

		EndTime = SDL_GetTicks();

		const int TimeDiff = EndTime - StartTime;

		if (17 > TimeDiff && TimeDiff > 0)
		{
			SDL_Delay(17 - TimeDiff);
			EndTime = SDL_GetTicks();
		}
	}
}

void SCRGame::Quit()
{
	m_ShouldQuit = true;
}

void SCRGame::Clear(const char& Red, const char& Green, const char& Blue)
{
	const int Color = SDL_MapRGB(m_pScreen->format, Red, Green, Blue);
	SDL_Rect Rect = { 0, 0, SCR_SCREEN_WIDTH, SCR_SCREEN_HEIGHT };
	SDL_FillRect(m_pScreen, &Rect, Color);
}

void SCRGame::CreateSharedData()
{
	assert(NULL == m_pSharedData);

	if (NULL == m_pSharedData)
	{
		m_pSharedData = new SCRGameData();
	}
}

void SCRGame::DestroySharedData()
{
	if (m_pSharedData)
	{
		delete m_pSharedData;
		m_pSharedData = NULL;
	}
}
